Skip to content

feat(logging): per-session log files for isolated debugging - #112

Merged
Acharnite merged 1 commit into
masterfrom
feat/per-session-logging
Jun 25, 2026
Merged

feat(logging): per-session log files for isolated debugging#112
Acharnite merged 1 commit into
masterfrom
feat/per-session-logging

Conversation

@Acharnite

@Acharnite Acharnite commented Jun 25, 2026

Copy link
Copy Markdown
Owner

Per-Session Log Files

What: Each session now writes its own dedicated log file at logs/session-<id>.log, keeping output fully isolated per concurrent session.

Why: Prior to this change, all sessions shared a single log stream, producing interleaved output that was difficult to trace during concurrent debugging. Per-session files give developers a clean, session-scoped view of log output.

How: A new module observability/session_logging.py provides setup_session_logging(session_id) and teardown_session_logging(). These are wired into _run_session() in sessions.py so every session gets automatic log isolation on creation and cleanup on teardown.

Safety: Failure to create or write to a per-session log file degrades gracefully — the session continues without interruption and falls back to a console warning. No session is ever blocked by a logging error.

Docs:

  • ADR-0003 updated to cover the per-session logging decision
  • Design doc §6.5 added to document the logging architecture

Files changed:

  • observability/session_logging.py — new module (setup/teardown helpers)
  • sessions.py — integration into _run_session()
  • docs/adr/ADR-0003-deepresearch-architecture.md — updated
  • docs/design/README.md — §6.5 added

Summary by CodeRabbit

  • New Features

    • Added automatic per-session log files for sessions, alongside the existing shared logs.
    • Session logging now includes clearer session context and continues even if per-session log setup fails.
  • Documentation

    • Updated architecture and design docs to reflect the latest session logging behavior and version metadata.
  • Bug Fixes

    • Ensured per-session logging is cleaned up when a session ends.

@coderabbitai

coderabbitai Bot commented Jun 25, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough

Walkthrough

The PR adds per-session file logging for DeepResearch sessions. It introduces helpers that create logs/session-<session_id>.log, inject session_id into log records, and tear down handlers after each session. Session startup now enables this logging, and the ADR/design docs describe the new behavior.

Changes

Per-session file logging

Layer / File(s) Summary
Session logging helpers
src/deepresearch/observability/session_logging.py
Introduces SessionFilter, project-relative log directory resolution, per-session FileHandler setup, and handler teardown for logs/session-<session_id>.log.
Session lifecycle wiring
src/deepresearch/web/sessions.py
MultiSessionManager._run_session starts per-session logging at session entry and tears it down in finally, logging warnings if setup or teardown fails.
Documentation updates
docs/adr/ADR-0003-web-frontend-and-multi-session.md, docs/design/README.md
Updates version metadata and adds the per-session logging behavior, scope, lifecycle, and consequence notes.

Sequence Diagram(s)

sequenceDiagram
  participant RunSession as MultiSessionManager._run_session
  participant Setup as setup_session_logging
  participant RootLogger as root logger
  participant FileHandler
  participant SessionLog as logs/session-<session_id>.log
  participant Teardown as teardown_session_logging

  RunSession->>Setup: session_id, topic
  Setup->>RootLogger: add FileHandler with SessionFilter
  Setup->>FileHandler: emit session-start delimiter
  FileHandler->>SessionLog: append formatted record
  RunSession->>Teardown: handler
  Teardown->>RootLogger: remove handler
  Teardown->>FileHandler: close()
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

A bunny hopped through logs so bright,
One session, one file, neat and light.
With session_id in every line,
My burrow keeps its traces fine.
🐇✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding per-session log files for isolated debugging.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/per-session-logging

Comment @coderabbitai help to get the list of available commands.

@Acharnite Acharnite left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Summary

Verdict: APPROVE with nits — The implementation is correct, well-structured, and follows best practices. A few minor issues noted below.


Step 3 — PR Description Review

Minor issues:

  1. Missing placeholder in path: The description reads:

    logs/session-.log

    This is missing the <session_id> placeholder. Should be:

    logs/session-<session_id>.log

  2. Missing topic parameter: The description says setup_session_logging(session_id) but the actual function signature is setup_session_logging(session_id, topic). The topic parameter (used for the initial delimiter line) is omitted from the description.


Step 4 — Code Review

observability/session_logging.py

  • Path resolution (_get_log_dir): Uses Path(__file__).resolve().parent.parent.parent.parent / "logs"identical to server.py line 34. Consistent. ✅
  • SessionFilter: Clean logging.Filter subclass. Injects session_id into LogRecord correctly. ✅
  • setup_session_logging: Properly creates directory, configures FileHandler with DEBUG level, sets formatter, adds SessionFilter, registers on root logger. Writes initial delimiter line using handler.handle() (which runs filters — correct approach). ✅
  • teardown_session_logging: Removes handler from root logger and closes it. Idempotent-safe. ✅
  • Error handling: mkdir and FileHandler raise OSError (docstring accurate). ✅

sessions.py_run_session() wiring ✅

Good:

  • Setup in try/except with a clear warning message and exc_info=True
  • Teardown in finally block ✅
  • Lazy import pattern consistent with the rest of the module ✅
  • _session_log_handler: logging.Handler | None type annotation ✅

One structural concern (minor):

There is a gap between the setup block (lines 297–310) and the main try: block (line 325). Lines 317–324 (cancel event creation + lazy imports) execute outside the inner try/finally. If those intermediate lines raise, the per-session handler would leak (added to root logger but never removed).

# Lines 297-310: setup (ok, caught)
_session_log_handler = ...  # might succeed

# Lines 317-324: intermediate code (NOT covered)
cancel_event = asyncio.Event()     # very unlikely to fail
from deepresearch.orchestrator ... # very unlikely to fail

# Line 325: inner try/finally starts
try:
    ...
finally:
    # teardown only runs if we reached line 325
    if _session_log_handler:
        teardown_session_logging(_session_log_handler)

In practice, asyncio.Event() and from imports virtually never raise. But if they did (e.g., import error from a dependency change), the handler would leak. To be maximally robust, consider wrapping the entire post-setup body in a single try/finally, or adding an additional guard. Not a blocker.

ADR-0003 & Design Doc Updates ✅

Accurate and consistent with code:

  • Version bumped to 1.4 (ADR) and 1.6 (design doc) ✅
  • Per-session enhancement noted in ADR §File-Based Logging section ✅
  • Design doc §6.5 matches implementation: format, level, lifecycle, safety ✅
  • New positive/negative consequences listed ✅

One minor doc mismatch:

Both documents state that the per-session log captures "all loggers under the deepresearch.* namespace." However, the handler is added to the root logger (logging.getLogger()), meaning all loggers (including third-party) will write to the per-session file. This is consistent with how the global deepresearch.log handler works (also on root logger), so it's behaviorally correct — but the "scope" description in §6.5 is technically narrower than reality. Consider updating §6.5 to say "the root logger (which includes all deepresearch.* loggers)."

ADR-0049 / The Ladder Compliance

Lean already. Ship.
  • Stdlib only: No new dependencies introduced. logging.Filter, logging.FileHandler, pathlib.Path — all stdlib. ✅
  • No unnecessary abstractions: SessionFilter is a minimal, idiomatic logging.Filter subclass. _get_log_dir() exists only to share path resolution with server.py. No interfaces, factories, or speculative generality. ✅
  • No ponytail comments needed: The implementation is simple enough that intentional-shortcut annotations are unnecessary. ✅

"Not lazy about" check ✅

  • Trust-boundary validation: session_id is auto-generated (uuid.uuid4()[:8]), not user-supplied. No injection risk. ✅
  • Data-loss error handling: Graceful degradation — setup failure warns and continues. ✅
  • Security: Log paths derived from internal IDs, not user input. ✅

Tests

No test file for the new module was found. The existing .testers_done marker suggests testers have signed off, but session_logging.py has 114 lines of new production code without dedicated tests. Consider adding basic tests for setup_session_logging (happy path + error path) and teardown_session_logging (including idempotency).


Summary of Issues

Severity Issue Location
🟢 Nit PR description: logs/session-.log missing <session_id> PR body
🟢 Nit PR description: omits topic parameter from function signature PR body
🟢 Nit Leak gap between setup block and inner try/finally sessions.py:310-325
🟢 Nit Doc scope says "deepresearch.*" but root logger captures all docs/design/README.md §6.5
🟡 Minor No tests for 114 lines of new production code

None of these are blockers. The code is correct, well-documented, and ready to merge.

@Acharnite
Acharnite merged commit 65b7b26 into master Jun 25, 2026
1 of 5 checks passed
@Acharnite
Acharnite deleted the feat/per-session-logging branch June 25, 2026 21:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant